#include "MarsPrecompile.h"

#include "MarsSceneMgr.h"
#include "MarsGameObject.h"
#include "MarsRenderer.h"
#include "MarsRenderable.h"
#include "MarsMesh.h"
#include "MarsMaterial.h"
#include "MarsVertexData.h"
#include "MarsCamera.h"
#include "MarsInputLayout.h"
#include "MarsVRamBuffer.h"
#include "MarsTexture.h"
#include "MarsSamplerState.h"
#include "MarsDirectionalLight.h"

using namespace SceneGraph;
using namespace Graphics;
using namespace GObject;

SceneMgr* SceneMgr::ms_pSceneMgr = 0;

void SceneMgr::Destroy()
{
	SAFE_DELETE( ms_pSceneMgr );
}

SceneMgr::SceneMgr()
	: m_pRootNode( NULL )
{

}

SceneMgr::~SceneMgr()
{
	Shutdown();
}

bool SceneMgr::Initialize()
{
	m_pRootNode = new SceneNode( "Root" );

	return true;
}

void SceneMgr::Shutdown()
{
	DestroyAllSceneNodes();

	SAFE_DELETE( m_pRootNode );
}

void SceneMgr::DestroyAllSceneNodes()
{
	SceneNodeMapItr it = m_mapSceneNodes.begin();
	SceneNodeMapItr end = m_mapSceneNodes.end();

	for ( ; it != end; ++it )
	{
		SAFE_DELETE( it->second );
	}

	m_mapSceneNodes.clear();
}

GameObject* SceneMgr::CreateGameObject()
{
	GameObject* pGameObject = new GameObject( m_pRootNode );

	m_mapSceneNodes[ pGameObject->GetName() ] = pGameObject;

	return pGameObject;
}

GameObject* SceneMgr::CreateGameObject( const std::string& strName )
{
	SceneNodeMapConstItr it = m_mapSceneNodes.find( strName );

	if ( it != m_mapSceneNodes.end() )
		return NULL;

	GameObject* pGameObject = new GameObject( strName, m_pRootNode );

	m_mapSceneNodes[ strName ] = pGameObject;

	return pGameObject;
}

void SceneMgr::DestroyGameObject( const std::string& strName )
{
	SceneNodeMapItr it = m_mapSceneNodes.find( strName );

	if ( it == m_mapSceneNodes.end() )
		return;

	SAFE_DELETE( it->second );

	m_mapSceneNodes.erase( it );
}

void SceneMgr::DestroyGameObject( GameObject* pGameObject )
{
	DestroyGameObject( pGameObject->GetName() );
}

GameObject* SceneMgr::FindGameObject( const std::string& strName )
{
	SceneNodeMapItr it = m_mapSceneNodes.find( strName );

	if ( it == m_mapSceneNodes.end() )
		return NULL;

	return ( GameObject* )( it->second );
}

void SceneMgr::Update( float dt )
{
	XMMATRIX matRootWorld = XMMatrixIdentity();
	m_pRootNode->Update( matRootWorld );
}

void SceneMgr::GetLights( LightsList& list )
{

}

void SceneMgr::Render( const Camera& camera )
{
	RenderObjectArray arrRenderObjects;

	m_pRootNode->Render( camera, arrRenderObjects );

	ID3D11DeviceContext* pContext = Renderer::Instance().GetDeviceContext();
	
	Renderable* pRenderable = NULL;
	Mesh* pMesh = NULL;
	Material* pMaterial = NULL;
	VertexData* pVertexData = NULL;

	for ( Core::uint32 i = 0; i < arrRenderObjects.size(); ++i )
	{
		pRenderable = arrRenderObjects[i]->GetRenderable();
		pMesh = pRenderable->GetMesh();
		pMaterial = pRenderable->GetMaterial();

		VertexData* pVertexData = pMesh->GetVertexData();

		pContext->IASetInputLayout( pVertexData->GetInputLayout()->GetNativeInputLayout() );

		ID3D11Buffer* pBuffer = pVertexData->GetVertexBuffer()->GetNativeBuffer();
		Core::uint32 iStride = pMesh->GetStride();
		Core::uint32 iOffset = pMesh->GetOffset();
		pContext->IASetVertexBuffers( 0, 1, &pBuffer, &iStride, &iOffset );

		if ( pVertexData->IsContainIndexBuffer() )
		{
			ID3D11Buffer* pIndexBuffer = pVertexData->GetIndexBuffer()->GetNativeBuffer();
			DXGI_FORMAT eFormat = pVertexData->GetIndexBufferFormat();
			pContext->IASetIndexBuffer( pIndexBuffer, eFormat, 0 );
		}

		pContext->IASetPrimitiveTopology( pMesh->GetTopology() );

		pContext->VSSetShader( pMaterial->GetVertexShader()->GetNativeVertexShader(), 0, 0 );
		pContext->PSSetShader( pMaterial->GetPixelShader()->GetNativePixelShader(), 0, 0 );

		ID3D11ShaderResourceView* pDiffuseMapSRV = pMaterial->GetDiffuseMap()->GetNativeTextureSRV();
		pContext->PSSetShaderResources( 0, 1, &pDiffuseMapSRV );
		ID3D11SamplerState* pDiffuseSS = pMaterial->GetDiffuseSamplerState()->GetNativeSamplerState();
		pContext->PSSetSamplers( 0, 1, &pDiffuseSS);

		XMMATRIX rotationMat = XMMatrixRotationRollPitchYaw( 0.0f, 0.7f, 0.7f );
		XMMATRIX translationMat = XMMatrixTranslation( 0.0f, 0.0f, 6.0f );

		XMMATRIX worldMatrix = rotationMat * translationMat;
		XMMATRIX viewMatrix = camera.GetViewMatrix();
		XMMATRIX projMatrix = camera.GetProjMatrix();
		XMMATRIX worldViewProjMatrix = worldMatrix * viewMatrix * projMatrix;

		worldMatrix = XMMatrixTranspose( worldMatrix );
		viewMatrix = XMMatrixTranspose( viewMatrix );
		projMatrix = XMMatrixTranspose( projMatrix );
		worldViewProjMatrix = XMMatrixTranspose( worldViewProjMatrix );

		XMFLOAT3 eyePos = camera.GetPosition();
		XMFLOAT4 pos( eyePos.x, eyePos.y, eyePos.z, 0.0f );
		pMaterial->SetParameter( SS_EyeWorldPosition, &pos );
		pMaterial->SetParameter( SS_WorldMatrix, &worldMatrix );
		pMaterial->SetParameter( SS_ViewMatrix, &viewMatrix );
		pMaterial->SetParameter( SS_ProjMatrix, &projMatrix );
		pMaterial->SetParameter( SS_WorldViewProjMatrix, &worldViewProjMatrix );

		XMMATRIX A = worldMatrix;
		A.r[ 3 ] = XMVectorSet( 0.0f, 0.0f, 0.0f, 1.0f );
		XMVECTOR det = XMMatrixDeterminant( A );
		A = XMMatrixTranspose( XMMatrixInverse( &det, A ) );
		pMaterial->SetParameter( SS_WorldInvTransposeMatrix, &A );

		ShaderDirectionalLight litParam;
		litParam.Ambient = s_pDirectionLight->GetAmbientColor();
		litParam.Diffuse = s_pDirectionLight->GetDiffuseColor();
		litParam.Specular = s_pDirectionLight->GetDiffuseColor();
		litParam.Direction = s_pDirectionLight->GetDirection();
		litParam.Pad = 0.0f;

		pMaterial->SetParameter( SS_DirectionalLight, &litParam );

		pMaterial->Bind();

		if ( pVertexData->IsContainIndexBuffer() )
		{
			pContext->DrawIndexed( pMesh->GetVertexCount(), 0, 0 );
		}
		else
		{
			pContext->Draw( pMesh->GetVertexCount(), 0 );
		}

		pMaterial->Unbind();
	}
}
